#pragma once
#include <assert.h>
#include <functional>
#include <unordered_map>
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Utility.hpp"
#include "Protocol.hpp"
const static uint16_t gport = 8080;
const static int bsize = 1024;

using namespace pr;
class Connection;
class EpollServer;
using func_t = std::function<void(Connection *, Request &)>;
using callback_t = std::function<void(Connection *)>;

// 每一个fd都有自己的缓冲区
class Connection
{
public:
    Connection(const int &fd, const std::string &clientip, const uint16_t &clientport)
        : fd_(fd), clientip_(clientip), clientport_(clientport)
    {
    }
    void Register(callback_t reacv, callback_t send, callback_t excepet)
    {
        reacv_ = reacv;
        send_ = send;
        excepet_ = excepet;
    }
    ~Connection()
    {
    }

public:
    // IO信息
    int fd_;
    std::string inbuffer_;
    std::string outbuffer_;

    // 用户信息,only debug
    std::string clientip_;
    uint16_t clientport_;

    // 回调函数
    callback_t reacv_;
    callback_t send_;
    callback_t excepet_;

    // 获取目前文件属于什么事件
    uint32_t ev;

    // 回指指针
    EpollServer *R;
};

class EpollServer
{
    const static int maxsize = 20;

public:
    EpollServer(func_t func, uint16_t port = gport) : func_(func), port_(port)
    {
    }
    void InitServer()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        ep_.Create();                                       // 创建epoll实例
        AddConnection(listensock_.Fd(), EPOLLIN | EPOLLET); // 变成ET模式
    }
    // 事件派发器
    void Dispatcher()
    {
        while (true)
        {
            // 1.能够直接进行accept吗？不行，需要epoll进行管理
            int timeout = -1;
            LoopOnce(timeout);
        }
    }
    void LoopOnce(int timeout)
    {
        int n = ep_.Wait(events_, maxsize, timeout);
        // logMessage(Debug, "有%d个事件就绪", n);
        for (int i = 0; i < n; i++)
        {
            // logMessage(Fatal, "新的连接到来...");
            int fd = events_[i].data.fd;
            uint32_t event = events_[i].events;
            logMessage(Debug, "当前正在处理%d上的%s", fd, (event & EPOLLIN) ? "EPOLLIN" : "OTHER");
            if (event & EPOLLIN)
                connections_[fd]->reacv_(connections_[fd]);
            if (event & EPOLLOUT)
                connections_[fd]->send_(connections_[fd]);
            if ((event & EPOLLERR) || (event & EPOLLHUP))
                connections_[fd]->excepet_(connections_[fd]);
        }
    }
    void AddConnection(int fd, uint32_t ev, std::string ip = "127.0.0.1", uint16_t port = gport)
    {
        // 1.将文件设置为非阻塞式文件
        Utility::SetNonBlock(fd);
        // 2.创建connection对象，对connections进行管理
        Connection *con = new Connection(fd, ip, port);
        if (fd == listensock_.Fd())
        {
            con->Register(std::bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            con->Register(std::bind(&EpollServer::Recver, this, std::placeholders::_1),
                          std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                          std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
        }
        con->R = this;
        connections_.insert(std::pair<int, Connection *>(fd, con));
        // 3.将用户要关注的事件传递给内核
        connections_[fd]->ev = ev;
        bool r = ep_.AddMovEvent(fd, ev, EPOLL_CTL_ADD); // 添加读事件
        assert(r);
        (void)r;                                                  // 使调试通过
        logMessage(Debug, "fd:%d,[%s:%d]", fd, ip.c_str(), port); // 查看调试
    }
    bool EnbleWriteRead(Connection *con, bool readable, bool writeable)
    {
        con->ev = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        return ep_.AddMovEvent(con->fd_, con->ev, EPOLL_CTL_MOD); // 让内核关心某些事件
    }
    void Accepter(Connection *con) // 注意：ET模式下的accpter需要进行读取--》①保证所有的连接都读取到
    {
        do
        {
            std::string clientip;
            uint16_t clientport;
            int err = 0;
            int sock = listensock_.Accept(&clientip, &clientport, &err);
            // 1.1此时这里能不能进行read/recv呢？不行，只有epoll知道sock上面的事件情况，将sock添加到epoll中
            if (sock > 0)
            {
                logMessage(Debug, "%s:%d 已经连上服务器", clientip.c_str(), clientport);
                AddConnection(sock, EPOLLIN | EPOLLET, clientip, clientport);
            }
            else
            { // 三种情况：①文件描述符下没有数据 ②事件中断 ③出现错误
                if (err == EAGAIN || err == EWOULDBLOCK)
                    break; // 直到文件描述符里面没有数据时才退出
                else if (err == EINTR)
                    continue;
                else
                {
                    logMessage(Warning, "errstring:%s,errcode:%d", strerror(err), err);
                    continue;
                }
            }
        } while (con->ev & EPOLLET);
        logMessage(Debug, "fd:%d->sock,accept done...", con->fd_);
    }
    // 读取
    // void Recver(int fd)
    // {
    //     char request[1024];
    //     ssize_t s = recv(fd, request, sizeof(request) - 1, 0); // 不会被阻塞
    //     if (s > 0)
    //     {
    //         // ServerIO
    //         request[s - 1] = 0;
    //         request[s - 2] = 0;                     // 这个xshell字符串以"\r\n"结尾
    //         connections_[fd]->inbuffer_ += request; // 将请求信息放入缓冲区

    //         std::cout << "client#" << request << std::endl;
    //         // std::string message = buffer;
    //         // message += "[epoll server echo]\r\n";
    //         // send(fd, message.c_str(), message.size(), 0);

    //         // 小型服务器初版
    //         std::string response = func_(request);
    //         send(fd, response.c_str(), response.size(), 0);
    //     }
    //     else
    //     {
    //         if (s == 0)
    //         {
    //             logMessage(Info, "client quit...");
    //         }
    //         else
    //         {
    //             logMessage(Warning, ",recv error,code:%d,string:%s", errno, strerror(errno));
    //         }
    //         // 注意：先从epoll中移除fd，再close fd
    //         ep_.DelEvent(fd);
    //         close(fd);
    //     }
    // }

    // 先获取文件描述符中的数据(需求)--》第二步保证读取的数据是完整--》发送结果(响应)
    // 循环读取
    void Recver(Connection *con)
    {
        do
        {
            char buffer[bsize];
            ssize_t s = recv(con->fd_, buffer, sizeof(buffer) - 1, 0); // 此时的文件描述符已经被设置为非阻塞了
            if (s > 0)
            {
                buffer[s] = 0;
                con->inbuffer_ += buffer; // 将读到的数据保存到缓冲区中
                // logMessage(Debug,"buffer:%s,[%d]",con->inbuffer_.c_str(),con->inbuffer_.size());

                // 第二步：怎么保证我们读到的数据是完整的呢？
                std::string request;
                int n = pr::BackPacket(con->inbuffer_, &request);
                if (n > 0)
                { // 已经获取到了完整报文-->进行业务逻辑处理
                    request =  pr::RemoveHeader(request, n); //去掉报头
                    Request req;
                    req.Deserialize(request);
                    func_(con, req);
                }
                else if (n == 0)
                { // 未获取到完整报文，继续读取
                    continue;
                }
            }
            else if (s == 0) // 文件描述符里面没有数据了，交给异常处理
            {
                con->excepet_(con);
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 底层没有数据了退出
                else if (errno == EINTR)
                    continue; // 被其它信号中断了，重新读取
                else
                {
                    con->excepet_(con); // 出现异常
                    break;
                }
            }
        } while (con->ev & EPOLLET);
    }
    void Sender(Connection *con)
    {
        do
        {
            // ssize_t send(int sockfd, const void *buf, size_t len, int flags);
            ssize_t n = send(con->fd_, con->outbuffer_.c_str(), con->outbuffer_.size(), 0);
            if (n > 0)
            { // 数据发送成功，关闭写功能，将缓冲区里面的数据进行清理
                // 写功能按需打开
                con->outbuffer_.erase(0, n);
                if(con->outbuffer_.empty())  //当发送缓冲区为空时，关闭写事件
                {
                    EnbleWriteRead(con, true, false);
                    break;
                }else
                {
                    EnbleWriteRead(con, true, true);
                }
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 底层没有数据了退出
                else if (errno == EINTR)
                    continue; // 被其它信号中断了，重新读取
                else
                    con->excepet_(con); // 出现异常
            }
        } while (con->ev & EPOLLET);
    }
    void Excepter(Connection *con)
    {
    }

    ~EpollServer()
    {
        listensock_.Close();
        ep_.Close();
    }

private:
    uint16_t port_;
    Sock listensock_;
    Epoll ep_;
    struct epoll_event events_[maxsize];
    func_t func_;
    std::unordered_map<int, Connection *> connections_; // 管理每一个fd的缓冲区
};